﻿namespace SaveALot.CustomDateRecalc
{
    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Workflow.Activities;
    using System.Workflow.ComponentModel;

    using Microsoft.SharePoint;

    public partial class DateRecalculation : SequenceActivity
    {
        #region Fields

        public static DependencyProperty StoreIDProperty = DependencyProperty.Register
          ( "StoreID", typeof( string ), typeof( DateRecalculation ) );
        public static DependencyProperty StoreUrlProperty = DependencyProperty.Register
          ( "StoreUrl", typeof( string ), typeof( DateRecalculation ) );
        public static DependencyProperty TaskIDProperty = DependencyProperty.Register
          ( "TaskID", typeof( string ), typeof( DateRecalculation ) );

        #endregion Fields

        #region Properties

        [Category( "Custom Save-A-Lot Actions" ),
        Browsable( true )]
        [DesignerSerializationVisibility( DesignerSerializationVisibility.Visible )]
        public string StoreID
        {
            get
            {
                return ( ( string )
                    ( base.GetValue( DateRecalculation.StoreIDProperty ) ) );
            }
            set
            {
                base.SetValue( DateRecalculation.StoreIDProperty, value );
            }
        }

        [Category( "Custom Save-A-Lot Actions" ),
        Browsable( true )]
        [DesignerSerializationVisibility( DesignerSerializationVisibility.Visible )]
        public string StoreUrl
        {
            get
            {
                return ( ( string )
                    ( base.GetValue( DateRecalculation.StoreUrlProperty ) ) );
            }
            set
            {
                base.SetValue( DateRecalculation.StoreUrlProperty, value );
            }
        }

        [Category( "Custom Save-A-Lot Actions" ),
        Browsable( true )]
        [DesignerSerializationVisibility( DesignerSerializationVisibility.Visible )]
        public string TaskID
        {
            get
            {
                return ( ( string )
                    ( base.GetValue( DateRecalculation.TaskIDProperty ) ) );
            }
            set
            {
                base.SetValue( DateRecalculation.TaskIDProperty, value );
            }
        }

        #endregion Properties

        #region Methods

        public static DateTime AddBusinessDays( DateTime date, int days )
        {
            int direction = ( days < 0 ) ? -1 : 1;

            while ( days != 0 )
            {
                date = date.AddDays( direction );
                if ( date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday )
                {
                    days -= direction;
                }
            }

            return date;
        }

        public static int BusinessDaysUntil( DateTime firstDay, DateTime lastDay, params DateTime[ ] bankHolidays )
        {
            firstDay = firstDay.Date;
            lastDay = lastDay.Date;
            if ( firstDay > lastDay )
                throw new ArgumentException( "Incorrect last day " + lastDay );

            TimeSpan span = lastDay - firstDay;
            int businessDays = span.Days + 1;
            int fullWeekCount = businessDays / 7;

            if ( businessDays > fullWeekCount * 7 )
            {

                int firstDayOfWeek = ( int ) firstDay.DayOfWeek;
                int lastDayOfWeek = ( int ) lastDay.DayOfWeek;
                if ( lastDayOfWeek < firstDayOfWeek )
                    lastDayOfWeek += 7;
                if ( firstDayOfWeek <= 6 )
                {
                    if ( lastDayOfWeek >= 7 )// Both Saturday and Sunday are in the remaining time interval
                        businessDays -= 2;
                    else if ( lastDayOfWeek >= 6 )// Only Saturday is in the remaining time interval
                        businessDays -= 1;
                }
                else if ( firstDayOfWeek <= 7 && lastDayOfWeek >= 7 )// Only Sunday is in the remaining time interval
                    businessDays -= 1;
            }

            businessDays -= fullWeekCount + fullWeekCount;

            if ( bankHolidays != null )
            {
                foreach ( DateTime bankHoliday in bankHolidays )
                {
                    DateTime bh = bankHoliday.Date;
                    if ( firstDay <= bh && bh <= lastDay )
                        --businessDays;
                }
            }
            return businessDays;
        }

        protected override ActivityExecutionStatus Execute( ActivityExecutionContext executionContext )
        {
            try
            {
                using ( SPSite storeSite = new SPSite( StoreUrl ) )
                {
                    using ( SPWeb storeWeb = storeSite.OpenWeb( ) )
                    {
                        SPList dateHolderList = storeWeb.Lists[ "Date Calculations" ];
                        SPList currentTaskList = storeWeb.Lists[ "Stores and Tasks" ];

                        SPQuery taskQuery = new SPQuery( );
                        taskQuery.Query = "<Where><Eq><FieldRef Name='ID' /><Value Type='Counter'>" + TaskID + "</Value></Eq></Where>";

                        //This query gets the current task we are dealing with...
                        SPListItemCollection taskRecord = currentTaskList.GetItems( taskQuery );

                        if ( taskRecord.Count == 0 )
                            return ActivityExecutionStatus.Closed;

                        DateTime dtNewDate = DateTime.Parse( taskRecord[ 0 ][ "Target Completion Date" ].ToString( ) );

                        //This query gets the old targer comp date of the task...
                        SPQuery oldDateQuery = new SPQuery( );
                        oldDateQuery.Query = "<Where><Eq><FieldRef Name='Task_x0020_ID' /><Value Type='Number'>" + TaskID + "</Value></Eq></Where><OrderBy><FieldRef Name='ID' Ascending='False' /></OrderBy>";
                        SPListItemCollection oldDateItem = dateHolderList.GetItems( oldDateQuery );
                        if ( oldDateItem.Count != 1 )
                            return ActivityExecutionStatus.Closed;

                        SPListItem currentDateCalculationRecord = oldDateItem[ 0 ];
                        if ( DateTime.Parse( currentDateCalculationRecord[ "Old Date" ].ToString( ) ).ToShortDateString( ).Equals( dtNewDate.ToShortDateString( ) ) )
                            return ActivityExecutionStatus.Closed;

                        //Dates are different so lets recalc;
                        int dateDifference = 0;
                        if ( dtNewDate > DateTime.Parse( currentDateCalculationRecord[ "Old Date" ].ToString( ) ) )
                        {
                            dateDifference = BusinessDaysUntil( DateTime.Parse( currentDateCalculationRecord[ "Old Date" ].ToString( ) ), dtNewDate, null );
                        }
                        else
                        {
                            dateDifference = BusinessDaysUntil( dtNewDate, DateTime.Parse( currentDateCalculationRecord[ "Old Date" ].ToString( ) ), null );
                            dateDifference = dateDifference * -1;
                        }

                        int midPointDateDifference = ( int ) dateDifference / 2;
                        SPListItem currentTaskRecord = taskRecord[ 0 ];

                        currentTaskRecord[ "Midway Check Point" ] = AddBusinessDays( DateTime.Parse( currentTaskRecord[ "Target Start Date" ].ToString( ) ), midPointDateDifference ).ToShortDateString( );
                        currentTaskRecord.Update( );
                        currentTaskList.Update( );

                        currentDateCalculationRecord[ "Old Date" ] = dtNewDate.ToShortDateString( );
                        currentDateCalculationRecord.Update( );
                        dateHolderList.Update( );

                        string remainingTasksQuery = "<Where><And><Eq><FieldRef Name='Store_x0020_Record_x0020_ID' /><Value Type='Number'>" + StoreID + "</Value></Eq><Gt><FieldRef Name='ID' /><Value Type='Counter'>" + TaskID + "</Value></Gt></And></Where><OrderBy><FieldRef Name='ID' Ascending='True' /></OrderBy>";
                        SPQuery remainingTasks = new SPQuery( );
                        remainingTasks.Query = remainingTasksQuery;
                        SPListItemCollection remainingTaskItems = currentTaskList.GetItems( remainingTasks );

                        int middleDateDiffence = 0;

                        foreach ( SPItem taskItem in remainingTaskItems )
                        {
                            DateTime dtCurrentCompletionDate = DateTime.Parse( taskItem[ "Target Completion Date" ].ToString( ) );
                            DateTime dtCurrentStartDate = DateTime.Parse( taskItem[ "Target Start Date" ].ToString( ) );

                            oldDateQuery = new SPQuery( );
                            oldDateQuery.Query = "<Where><Eq><FieldRef Name='Task_x0020_ID' /><Value Type='Number'>" + taskItem[ "ID" ].ToString( ) + "</Value></Eq></Where><OrderBy><FieldRef Name='Task_x0020_ID' Ascending='False' /></OrderBy>";
                            oldDateItem = dateHolderList.GetItems( oldDateQuery );
                            if ( oldDateItem.Count != 1 )
                                return ActivityExecutionStatus.Closed;

                            currentDateCalculationRecord = oldDateItem[ 0 ];

                            string endDateString = AddBusinessDays( dtCurrentCompletionDate, dateDifference ).ToShortDateString( );
                            string startDateString = AddBusinessDays( dtCurrentStartDate, dateDifference ).ToShortDateString( );

                            taskItem[ "Target Completion Date" ] = endDateString;
                            taskItem[ "Target Start Date" ] = startDateString;

                            if ( DateTime.Parse( endDateString ) > DateTime.Parse( startDateString ) )
                            {
                                middleDateDiffence = BusinessDaysUntil( DateTime.Parse( startDateString ), DateTime.Parse( endDateString ), null ) / 2;
                            }
                            else
                            {
                                middleDateDiffence = BusinessDaysUntil( DateTime.Parse( endDateString ), DateTime.Parse( startDateString ), null ) / 2;
                                middleDateDiffence = middleDateDiffence * -1;
                            }

                            taskItem[ "Midway Check Point" ] = AddBusinessDays( DateTime.Parse( startDateString ), middleDateDiffence ).ToShortDateString( );
                            taskItem.Update( );
                            currentTaskList.Update( );

                            currentDateCalculationRecord[ "Old Date" ] = endDateString;
                            currentDateCalculationRecord.Update( );
                            dateHolderList.Update( );
                        }
                    }
                }
            }
            catch ( Exception ex )
            {
                EventLog.WriteEntry( "Task Creation Workflow", ex.ToString( ) );
            }

            return ActivityExecutionStatus.Closed;
        }

        private static int DateDifference( DateTime oldDate, DateTime newDate )
        {
            TimeSpan ts = newDate - oldDate;

                int days = ts.Days;
                return days;
        }

        #endregion Methods
    }
}